#include "../include/gba/defines.h"
#include "decompress.h"
#include "../include/sprite.h"
#include "../include/constants/songs.h"
#include "../include/menu.h"
#include "../include/new_menu_helpers.h"
#include "../include/string_util.h"
#include "../include/sound.h"

#define MALE_X 70
#define MALE_Y 65
#define FEMALE_X (MALE_X + 100)
#define FEMALE_Y (MALE_Y)
#define X_OFFSET (FEMALE_X - MALE_X)
#define ARROW_X (MALE_X)
#define ARROW_Y (MALE_Y)
#define TAG_MALE 114
#define TAG_FEMALE 514
#define TAG_ARROW 721

#define ANIMCMD_FRAME(...) \
    {.frame = {__VA_ARGS__}}
#define ANIMCMD_LOOP(_count) \
    {.loop = {.type = -3, .count = _count}}
#define ANIMCMD_JUMP(_target) \
    {.jump = {.type = -2, .target = _target}}
#define ANIMCMD_END \
    {.type = -1}

void Task_OakSpeech_FadeOutOak(u8 taskId);
static void Task_OakSpeech_GenderChooseClear(u8 taskid);
static void Task_OakSpeech_GenderChooseInput(u8 taskid);
static void Task_OakSpeech_GenderChooseInit(u8 taskid);
static void SpriteCB_Arrow(struct Sprite *sprite);
static void SpriteCB_Dummy(struct Sprite *sprite);


enum {
    FONT_SMALL,
    FONT_NORMAL_COPY_1,
    FONT_NORMAL,
    FONT_NORMAL_COPY_2,
    FONT_MALE,
    FONT_FEMALE,
    FONT_BRAILLE,
    FONT_BOLD,
};

#define TEXT_COLOR_TRANSPARENT  0x0
#define TEXT_COLOR_WHITE        0x1
#define TEXT_COLOR_DARK_GRAY    0x2
#define TEXT_COLOR_LIGHT_GRAY   0x3
#define TEXT_COLOR_RED          0x4
#define TEXT_COLOR_LIGHT_RED    0x5
#define TEXT_COLOR_GREEN        0x6
#define TEXT_COLOR_LIGHT_GREEN  0x7
#define TEXT_COLOR_BLUE         0x8
#define TEXT_COLOR_LIGHT_BLUE   0x9
#define TEXT_DYNAMIC_COLOR_1    0xA // Usually white
#define TEXT_DYNAMIC_COLOR_2    0xB // Usually white w/ tinge of green
#define TEXT_DYNAMIC_COLOR_3    0xC // Usually white
#define TEXT_DYNAMIC_COLOR_4    0xD // Usually aquamarine
#define TEXT_DYNAMIC_COLOR_5    0xE // Usually blue-green
#define TEXT_DYNAMIC_COLOR_6    0xF // Usually cerulean

void __attribute__((long_call)) Task_OakSpeech_LoadPlayerPic(u8);

#define OakSpeechPrintMessage(str, speed) ({                                                                                                                 \
    DrawDialogueFrame(WIN_INTRO_TEXTBOX, FALSE);                                                                                                             \
    if (str != gStringVar4)                                                                                                                                  \
    {                                                                                                                                                        \
        StringExpandPlaceholders(gStringVar4, str);                                                                                                          \
        AddTextPrinterParameterized2(WIN_INTRO_TEXTBOX, FONT_MALE, gStringVar4, speed, NULL, TEXT_COLOR_DARK_GRAY, TEXT_COLOR_WHITE, TEXT_COLOR_LIGHT_GRAY); \
    }                                                                                                                                                        \
    else                                                                                                                                                     \
    {                                                                                                                                                        \
        AddTextPrinterParameterized2(WIN_INTRO_TEXTBOX, FONT_MALE, str, speed, NULL, TEXT_COLOR_DARK_GRAY, TEXT_COLOR_WHITE, TEXT_COLOR_LIGHT_GRAY);         \
    }                                                                                                                                                        \
    CopyWindowToVram(WIN_INTRO_TEXTBOX, 3);                                                                                                       \
})

static const union AnimCmd gFemaleSpriteAnim2[]=
{
    ANIMCMD_FRAME(0,5),
    ANIMCMD_FRAME(64,5),
    ANIMCMD_FRAME(0,5),
    ANIMCMD_END
};

static const union AnimCmd gFemaleSpriteAnim1[]=
{
    ANIMCMD_FRAME(0, 1),
    ANIMCMD_END
};

static const union AnimCmd *const gFemaleSpriteAnimTable[] =
{
    [0] = gFemaleSpriteAnim1,
    [1] = gFemaleSpriteAnim2,
};

extern const struct OamData gOamData_AffineDouble_ObjNormal_64x64;
extern const struct OamData gOamData_AffineDouble_ObjNormal_16x16;

const u8 MaleTiles[] = INCBIN_U8("graphics/ChooseGender/Male.4bpp.lz");
const u8 FemaleTiles[] = INCBIN_U8("graphics/ChooseGender/Female.4bpp.lz");
const u8 angleTiles[] = INCBIN_U8("graphics/ChooseGender/angle.4bpp.lz");

const u8 MalePal[] = INCBIN_U8("graphics/ChooseGender/Male.gbapal.lz");
const u8 FemalePal[] = INCBIN_U8("graphics/ChooseGender/Female.gbapal.lz");
const u8 AnglePal[] = INCBIN_U8("graphics/ChooseGender/angle.gbapal.lz");

extern const u32 anglePal[];

static const struct SpriteTemplate MaleSpriteTemplate =
{
    .tileTag = TAG_MALE,
    .paletteTag = TAG_MALE,
    .oam = &gOamData_AffineDouble_ObjNormal_64x64,
    .anims = gFemaleSpriteAnimTable, // Use a valid animation table for the male sprite
    .images = NULL,
    .affineAnims = gDummySpriteAffineAnimTable,
    .callback = SpriteCB_Dummy,
};

static const struct SpriteTemplate FemaleSpriteTemplate =
{
    .tileTag = TAG_FEMALE,
    .paletteTag = TAG_FEMALE,
    .oam = &gOamData_AffineDouble_ObjNormal_64x64,
    .anims = gFemaleSpriteAnimTable,
    .images = NULL,
    .affineAnims = gDummySpriteAffineAnimTable,
    .callback = SpriteCB_Dummy,
};

static const struct SpriteTemplate ArrowSpriteTemplate = 
{
    .tileTag = TAG_ARROW,
    .paletteTag = TAG_ARROW,
    .oam = &gOamData_AffineDouble_ObjNormal_64x64,
    .anims = gDummySpriteAnimTable,
    .images = NULL,
    .affineAnims = gDummySpriteAffineAnimTable,
    .callback = SpriteCB_Dummy,
};

enum
{
    WIN_INTRO_TEXTBOX,
    WIN_INTRO_BOYGIRL,
    WIN_INTRO_YESNO,
    WIN_INTRO_NAMES,
    NUM_INTRO_WINDOWS,
};

struct OakSpeechResources
{
    void *oakSpeechBackgroundTiles;
    void *trainerPicTilemap;
    void *pikachuIntroTilemap;
    void *unused1;
    u16 hasPlayerBeenNamed;
    u16 currentPage;
    u16 windowIds[NUM_INTRO_WINDOWS];
    u8 textColor[3];
    u8 textSpeed;
    u8 unused2[0x1800];
    u8 bg2TilemapBuffer[0x400];
    u8 bg1TilemapBuffer[0x800];
};

extern EWRAM_DATA struct OakSpeechResources *sOakSpeechResources;

void Task_OakSpeech_GenderChooseClear(u8 taskid)
{
    DestroySprite(&gSprites[gTasks[taskid].data[10]]);
    DestroySprite(&gSprites[gTasks[taskid].data[11]]);
    DestroySprite(&gSprites[gTasks[taskid].data[12]]);
    gTasks[taskid].func = Task_OakSpeech_LoadPlayerPic;
}

static void SpriteCB_Dummy(struct Sprite *sprite)
{
}

static void SpriteCB_Arrow(struct Sprite *sprite)
{
    if(sprite->y > ARROW_Y && sprite->data[0]%10)
    {
        sprite->y -=5;
    }
    else if(sprite->data[0]%20==10)
    {
        sprite->y +=5;
    }
    sprite->data[0]++;
}


const struct CompressedSpriteSheet gMaleSpriteSheet = {
    .data = MaleTiles,
    .size = 64*64/2*2,
    .tag = TAG_MALE
};

const struct CompressedSpriteSheet gFemaleSpriteSheet = {
    .data = FemaleTiles,
    .size = 64*64/2*2,
    .tag = TAG_FEMALE
};

const struct CompressedSpriteSheet gAngleSpriteSheet = {
    .data = angleTiles,
    .size = 64*64/1*1,
    .tag = TAG_ARROW
};

static const struct CompressedSpritePalette sMalePalette =
{
    MalePal, TAG_MALE
};

static const struct CompressedSpritePalette sFemalePalette =
{
    FemalePal, TAG_FEMALE
};

static const struct CompressedSpritePalette sAnglePalette =
{
    AnglePal, TAG_ARROW
};

void __attribute__((long_call)) Task_OakSpeech_AskPlayerGender(u8 taskId);

static void Task_OakSpeech_GenderChooseInit(u8 taskid)
{
    if(!IsTextPrinterActive(WIN_INTRO_TEXTBOX))
    {
        LoadCompressedSpriteSheet(&gAngleSpriteSheet);
        LoadCompressedSpriteSheet(&gMaleSpriteSheet);
        LoadCompressedSpriteSheet(&gFemaleSpriteSheet);
        LoadCompressedSpritePalette(&sAnglePalette);
        LoadCompressedSpritePalette(&sMalePalette);
        LoadCompressedSpritePalette(&sFemalePalette);
        gTasks[taskid].data[10] = CreateSpriteAndAnimate(&ArrowSpriteTemplate,ARROW_X,ARROW_Y,90);
        gTasks[taskid].data[11] = CreateSpriteAndAnimate(&MaleSpriteTemplate,MALE_X,MALE_Y,80);
        gTasks[taskid].data[12] = CreateSpriteAndAnimate(&FemaleSpriteTemplate,FEMALE_X,FEMALE_Y,80);
        gTasks[taskid].func = Task_OakSpeech_GenderChooseInput;
    }
}

extern struct SaveBlock2 *gSaveBlock2Ptr;
void __attribute__((long_call)) DisplayYesNoMenuDefaultYes(void);

#define MENU_NOTHING_CHOSEN -2
#define MENU_B_PRESSED -1

static void Task_YesNoMenu_HandleInput(u8 taskId)
{
    s8 input;
    if (gTasks[taskId].data[3] != 0)
    {
        gTasks[taskId].data[3]--;
    }
    else
    {
        input = Menu_ProcessInputNoWrapClearOnChoose();
        switch (input)
        {
        case MENU_NOTHING_CHOSEN:
            return;
        case MENU_B_PRESSED:
        case 1: // NO
            PlaySE(SE_SELECT);
            gTasks[taskId].func = Task_OakSpeech_GenderChooseInput;
            break;
        case 0: // YES
            gTasks[taskId].func = Task_OakSpeech_GenderChooseClear;
            break;
        }
        
    }
}

static void Task_OakSpeech_GenderChooseInput(u8 taskid)
{
    if(IsTextPrinterActive(WIN_INTRO_TEXTBOX))
        return;
    u16 SpriteIdArrow1 = gTasks[taskid].data[10];
    if(JOY_NEW(DPAD_RIGHT))
    {
        if(gSprites[SpriteIdArrow1].x == MALE_X)
        {
            gSprites[SpriteIdArrow1].x += X_OFFSET;
            StartSpriteAnim(&gSprites[gTasks[taskid].data[12]],1);
            StartSpriteAnim(&gSprites[gTasks[taskid].data[11]],0);
        }
    }
    if(JOY_NEW(DPAD_LEFT))
    {
        if(gSprites[SpriteIdArrow1].x == FEMALE_X)
        {
            gSprites[SpriteIdArrow1].x -=X_OFFSET;
            StartSpriteAnim(&gSprites[gTasks[taskid].data[11]],1);
            StartSpriteAnim(&gSprites[gTasks[taskid].data[12]],0);
        }
    }
    if(JOY_NEW(A_BUTTON))
    {
        if(gSprites[SpriteIdArrow1].x == MALE_X)
        {
            gSaveBlock2Ptr->playerGender = MALE;
        }
        if(gSprites[SpriteIdArrow1].x == FEMALE_X)
        {
            gSaveBlock2Ptr->playerGender = FEMALE;
        }
        DisplayYesNoMenuDefaultYes();
        gTasks[taskid].data[3] = 5;
        gTasks[taskid].func = Task_YesNoMenu_HandleInput ;
    }
}

#define tSpriteTimer                data[0]
#define tTrainerPicPosX             data[1]
#define tTrainerPicFadeState        data[2]
#define tTimer                      data[3]
#define tNidoranFSpriteId           data[4]
#define tTextCursorSpriteId         data[5]
#define tPokeBallSpriteId           data[6]
#define tPikachuPlatformSpriteId(i) data[7 + i] // Pikachu and the platform are built of three sprites,
                                 // data[8]     // so these are used to hold their sprite IDs
                                 // data[9]     //
#define tMenuWindowId               data[13]
#define tTextboxWindowId            data[14]
#define tDelta                      data[15]

void __attribute__((long_call)) CreateFadeInTask(u8, u8);

void Task_OakSpeech_FadeOutOak(u8 taskId)
{
    s16 *data = gTasks[taskId].data;

    if (!IsTextPrinterActive(WIN_INTRO_TEXTBOX))
    {
        ClearDialogWindowAndFrame(WIN_INTRO_TEXTBOX, 1);
        CreateFadeInTask(taskId, 2);
        tTimer = 48;
        gTasks[taskId].func = Task_OakSpeech_GenderChooseInit;
    }
}

